<?php

namespace handle\message\lib\driver;

use Exception;
use handle\message\lib\driver\lib\Event;
use handle\message\lib\factory\SingletonFactory;
use handle\message\lib\manage\backups\Backups;
use handle\message\lib\manage\ChessBoardManage;
use handle\message\lib\manage\lib\ChessBoard;
use handle\message\lib\manage\lib\Player;
use handle\message\lib\manage\PlayerManage;
use handle\message\lib\manage\RoomManage;
use handle\message\lib\traits\ManageObserver;

class EventDriver
{
    use ManageObserver;

    /**
     * 房间管理
     * @var RoomManage
     */
    private RoomManage $room;

    /**
     * 玩家管理
     * @var PlayerManage
     */
    private PlayerManage $player;

    /**
     * 棋盘管理
     * @var ChessBoardManage
     */
    private ChessBoardManage $board;

    /**
     * 初始化
     * @return void
     */
    protected function initialization(){
        $this->room = SingletonFactory::getInstance()->makeInstances('room');
        $this->player = SingletonFactory::getInstance()->makeInstances('player');
        $this->board = SingletonFactory::getInstance()->makeInstances('board');
    }

    /**
     * 恢复数据
     * @param string $room_id
     * @return void
     * @throws Exception
     */
    public function restore(string $room_id)
    {
        if((new Backups())->setRoomID($room_id)->restore()){
            $this->room = SingletonFactory::getInstance()->makeInstances('room', true);
            $this->player = SingletonFactory::getInstance()->makeInstances('player', true);
            $this->board = SingletonFactory::getInstance()->makeInstances('board', true);
            $this->observer[$room_id] =  new Event($room_id);
            $this->room->isObserverIsset($room_id) and $this->observer[$room_id]->createRoom($room_id);
            $this->player->isObserverIsset($room_id) and $this->observer[$room_id]->createPlayers($room_id);
            $this->board->isObserverIsset($room_id) and $this->observer[$room_id]->createChessBoard($room_id);
        }
    }

    /**
     * 为房间创建事件
     * @param string $room_id
     * @return Event
     * @throws Exception
     */
    public function create(string $room_id): Event
    {
        $this->checkRoomIsset($room_id) or $this->observer[$room_id] =  new Event($room_id);
        $this->room->create($room_id);
        $this->observer[$room_id]->createRoom($room_id);
        return $this->observer[$room_id];
    }

    /**
     * 检测房间是否存在
     * @param string $room_id
     * @return bool
     */
    public function checkRoomIsset(string $room_id): bool
    {
        return isset($this->observer[$room_id]) and !empty($this->observer[$room_id]);
    }

    /**
     * 重连
     * @param string $room_id
     * @param int $uid
     * @return void
     * @throws Exception
     */
    public function reconnect(string $room_id, int $uid){
        $this->get($room_id)->reconnect($uid);
    }

    /**
     * 开始游戏
     * @param string $room_id
     * @param int $uid
     * @return void
     * @throws Exception
     */
    public function startGame(string $room_id, int $uid){
        $this->room->startGame($room_id);
        $this->get($room_id)->startGame($uid);
    }

    /**
     * 某个人进入房间
     * @param string $room_id
     * @param int $uid
     * @return void
     * @throws Exception
     */
    public function enterRoom(string $room_id, int $uid)
    {
        $this->room->enterRoom($room_id, $uid);
        $this->get($room_id)->enterRoom($uid);
    }

    /**
     * 退出房间
     * @param string $room_id
     * @param int $uid
     * @return void
     * @throws Exception
     */
    public function exitRoom(string $room_id, int $uid){
        $this->room->exitRoom($room_id, $uid);
        $this->get($room_id)->exitRoom($uid);
    }

    /**
     * 发送相关请求
     * @param string $room_id
     * @param int $uid
     * @param int $type 1:悔棋请求, 2:和棋请求
     * @return void
     * @throws Exception
     */
    public function sendRelatedRequest(string $room_id, int $uid, int $type){
        $this->get($room_id)->sendRelatedRequest($uid, $type);
    }

    /**
     * 发送拒绝通知
     * @param string $room_id
     * @param int $uid
     * @param int $type 1:悔棋拒绝, 2:和棋拒绝
     * @return void
     * @throws Exception
     */
    public function sendRefuseNotify(string $room_id, int $uid, int $type){
        $this->get($room_id)->sendRefuseNotify($uid, $type);
    }

    /**
     * 设置平局游戏状态
     * @param string $room_id 房间ID
     * @param int $uid       胜方ID
     * @return void
     * @throws Exception
     */
    public function gameTie(string $room_id, int $uid = 0){
        $this->room->setOutcome($room_id, 3);
        $this->get($room_id)->sendCompetitionResults($uid, 3);
    }

    /**
     * 认输
     * @param string $room_id
     * @param int $uid
     * @return void
     * @throws Exception
     */
    public function surrender(string $room_id, int $uid){
        $this->get($room_id)->sendCompetitionResults($uid, 5);
    }

    /**
     * 创建玩家
     * @param string $room_id
     * @param int $uid
     * @param string $name
     * @param int $homeowner
     * @return Player
     * @throws Exception
     */
    public function createPlayer(string $room_id, int $uid, string $name, int $homeowner): Player
    {
        $player = $this->player->create($room_id, $uid, $name, $homeowner);
        $this->get($room_id)->createPlayers($room_id);
        return $player;
    }

    /**
     * 创建连接器
     * @param string $room_id
     * @param int $uid
     * @param $connection
     * @return void
     * @throws Exception
     */
    public function createConnection(string $room_id, int $uid, $connection){
        $this->player->setConnection($room_id, $uid, $connection);
    }

    /**
     * 更新玩家信息
     * @param string $room_id
     * @param int $uid 玩家ID
     * @param int $chess 棋子
     * @param int $mover 先后顺序
     * @return void
     * @throws Exception
     */
    public function updatePlayer(string $room_id, int $uid, int $chess = 0, int $mover = 0)
    {
        $this->player->updatePlayer($room_id, $uid, $chess, $mover);  //游戏未开始，可以更新玩家信息
        $this->player->isHasTwoPlayers($room_id) and $this->get($room_id)->sendUsersInfos($this->get($room_id)->getOppositePlayer($uid), $this->get($room_id)->getMyPlayer($uid));
    }

    /**
     * 创建棋盘
     * @param string $room_id
     * @return ChessBoard
     * @throws Exception
     */
    public function createChessBoard(string $room_id): ChessBoard
    {
        $board = $this->board->create($room_id);
        $this->get($room_id)->createChessBoard($room_id);
        return $board;
    }

    /**
     * 更新棋盘
     * @param string $room_id 房间ID编号
     * @param string $x
     * @param string $y
     * @param int $uid
     * @return void
     * @Tips 如果落子位置已经有棋子，则提示已经落子
     * @throws Exception
     */
    public function updateBoard(string $room_id, string $x, string $y, int $uid)
    {
        if($this->board->checkSomeXY($room_id, $x, $y)){
            $this->get($room_id)->positionHasBeenOccupied($uid);
        }else{
            $this->board->updateBoard($room_id, $x, $y, $this->player->getPlayer($room_id, $uid)->getMyCanSendData('chess'), $uid);
            $this->get($room_id)->seedMyChessPosition();
        }
    }

    /**
     * 悔棋操作，将上一步的棋盘恢复
     * @param string $room_id
     * @param int $uid
     * @return void
     * @throws Exception
     */
    public function withdraw(string $room_id, int $uid){    //注意：进入这个里的玩家一定是对方玩家，而不是我自己，所以对方玩家的getOppositePlayer 就是我自己
        if($this->board->getRound($room_id) <= 0){
            $this->player->getOppositePlayer($room_id, $uid)->getConnection()->sendMsg('悔棋失败，当前棋局已无上一步棋'); //悔棋失败，当前棋局已无上一步棋
            return;
        }
        if($this->player->getOppositePlayer($room_id, $uid)->isMyTurn()){    //是我的回合  -> 不通过
            $this->player->getOppositePlayer($room_id, $uid)->getConnection()->sendMsg('无法悔棋，当前是您的回合');
            return;
        }
        if($this->player->getOppositePlayer($room_id, $uid)->isMyTurn() === false and $this->player->isMyTurn($room_id, $uid) === false){   //都不是双方回合，数据异常
            $this->player->getOppositePlayer($room_id, $uid)->getConnection()->sendMsg('玩家数据异常592');   //玩家回合错误
            return;
        }
        if(!$this->player->isMyTurn($room_id, $uid) or $this->player->getOppositePlayer($room_id, $uid)->getMyCanSendData('sendWithdraw') === 3){ //不是对方回合 or 我悔过棋   -> 不通过
            $this->player->getOppositePlayer($room_id, $uid)->getConnection()->sendMsg('非对方回合/您已悔过棋，无法操作');
            return;
        }
        //表示：不是我的回合 and 是对方回合 and 我没有悔过棋 才可以悔棋
        $this->board->withdraw($room_id);
        $this->get($room_id)->withdraw();
    }

//    /**
//     * 重置房间、玩家、棋盘类
//     * @param string $room_id
//     * @return void
//     * @throws Exception
//     */
//    public function resetRoomPlayerBoard(string $room_id){
//        $this->room->reset($room_id);
//        $this->player->reset($room_id);
//        $this->board->reset($room_id);
//    }

    /**
     * 获取管理器
     * @param string $instance
     * @return RoomManage|PlayerManage|ChessBoardManage|null
     */
    public function getManager(string $instance)
    {
        return in_array($instance, ['room', 'player', 'board', 'chat']) ? $this->{$instance} : null;
    }

    /**
     * 系统通知关闭房间
     * @param $room_id
     * @return void
     * @throws Exception
     */
    public function systemCloseRoom($room_id): void
    {
        $this->get($room_id)->systemCloseRoom();
    }

    /**
     * 系统通知结束棋局
     * @param $room_id
     * @return void
     * @throws Exception
     */
    public function systemEndGame($room_id): void
    {
        $this->get($room_id)->systemEndGame();
    }


}